//
// Created by 陈成 on 2019/9/19.
//

#include "InstR.h"

InstR::InstR(uint32_t pc, sc_bv<32> value, RegBank<REG_NUM> *r, RegBank<CSR_NUM> *csr, Mem *m) :
        Inst(pc, value, r, csr, m),
        funct7(value.range(31, 25)),
        rs2   (value.range(24, 20)),
        rs1   (value.range(19, 15)),
        funct3(value.range(14, 12)),
        rd    (value.range(11,  7)),
        opcode(value.range( 6,  0)) {

}

uint32_t
InstR::exec() {
    uint32_t pc_nxt = pc + 4;
    switch (opcode.to_uint()) {
    case 0b0010011:
        handleType0();
        break;
    default:
        assert(false);
        break;
    }

    return pc_nxt;
}

void InstR::handleType0() {
    uint32_t u32_rd = rd.to_uint();
    uint32_t u32_rs1 = r->read(rs1.to_uint()).to_uint();
    int32_t  i32_rs1 = r->read(rs1.to_uint()).to_int();
    uint32_t u32_rs2 = r->read(rs2.to_uint()).to_uint();
    int32_t  i32_rs2 = r->read(rs2.to_uint()).to_int();

    uint32_t rslt;
    switch ((funct7, funct3).to_uint()) {
    case 0b0000000000: // ADD
        rslt = u32_rs1 + u32_rs2;
        r->write(u32_rd, rslt);

        LOG("ADD RD: %d, RS1: %d, RS2: %d", rd.to_uint(), rs1.to_uint(), rs2.to_uint());
        LOG("%d + %d = %d", u32_rs1, u32_rs2, rslt);
        break;
    case 0b0000000001: // SLT
        rslt = (i32_rs1 < i32_rs2 ? 1 : 0);
        r->write(u32_rd, rslt);

        LOG("SLT RD: %d, RS1: %d, RS2: %d", rd.to_uint(), rs1.to_uint(), rs2.to_uint());
        LOG("%d < %d = %d", u32_rs1, u32_rs2, rslt);
        break;
    case 0b0000000010: // SLTU
        rslt = (u32_rs1 < u32_rs2 ? 1 : 0);
        r->write(u32_rd, rslt);

        LOG("SLTU RD: %d, RS1: %d, RS2: %d", rd.to_uint(), rs1.to_uint(), rs2.to_uint());
        LOG("%d < %d = %d", u32_rs1, u32_rs2, rslt);
        break;
    case 0b0000000011: // AND
        rslt = u32_rs1 & u32_rs2;
        r->write(u32_rd, rslt);

        LOG("AND RD: %d, RS1: %d, RS2: %d", rd.to_uint(), rs1.to_uint(), rs2.to_uint());
        LOG("%d & %d = %d", u32_rs1, u32_rs2, rslt);
        break;
    case 0b0000000100: // OR
        rslt = u32_rs1 | u32_rs2;
        r->write(u32_rd, rslt);

        LOG("OR RD: %d, RS1: %d, RS2: %d", rd.to_uint(), rs1.to_uint(), rs2.to_uint());
        LOG("%d | %d = %d", u32_rs1, u32_rs2, rslt);
        break;
    case 0b0000000101: // XOR
        rslt = u32_rs1 ^ u32_rs2;
        r->write(u32_rd, rslt);

        LOG("XOR RD: %d, RS1: %d, RS2: %d", rd.to_uint(), rs1.to_uint(), rs2.to_uint());
        LOG("%d ^ %d = %d", u32_rs1, u32_rs2, rslt);
        break;
    case 0b0000000110: // SLL
        rslt = u32_rs1 << (u32_rs2 & 0x1f);
        r->write(u32_rd, rslt);

        LOG("SLL RD: %d, RS1: %d, RS2: %d", rd.to_uint(), rs1.to_uint(), rs2.to_uint());
        LOG("%d << %d = %d", u32_rs1, u32_rs2 & 0x1F, rslt);
        break;
    case 0b0000000111: // SRL
        rslt = u32_rs1 >> (u32_rs2 & 0x1f);
        r->write(u32_rd, rslt);

        LOG("SRL RD: %d, RS1: %d, RS2: %d", rd.to_uint(), rs1.to_uint(), rs2.to_uint());
        LOG("%d >> %d = %d", u32_rs1, u32_rs2 & 0x1F, rslt);
        break;
    case 0b0100000000: // SUB
        rslt = u32_rs1 - u32_rs2;
        r->write(u32_rd, rslt);

        LOG("SUB RD: %d, RS1: %d, RS2: %d", rd.to_uint(), rs1.to_uint(), rs2.to_uint());
        LOG("%d - %d = %d", u32_rs1, u32_rs2, rslt);
        break;
    case 0b0100000111: // SRA
        rslt = u32_rs1 >> (u32_rs2 & 0x1f);
        rslt = rslt | (u32_rs1 & 0x80000000);
        r->write(u32_rd, rslt);

        LOG("SRA RD: %d, RS1: %d, RS2: %d", rd.to_uint(), rs1.to_uint(), rs2.to_uint());
        LOG("%d >> %d = %d", u32_rs1, u32_rs2 & 0x1F, rslt);
        break;
    default:
        assert(false);
        break;
    }
}
